package net.pws.common.security.filter;

import javax.servlet.Filter;

import net.pws.common.util.StringUtils;
import net.pws.common.security.spi.AuthenticateProvider;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;


public class AuthFilterChainFactoryBean implements
                                       FactoryBean<Filter>,
                                       InitializingBean {
    
    private AuthenticateProvider authenticateProvider;
    
    private ClientVersionProvider clientVersionProvider;
    
    private CaptchaValidator captchaValidator;
    
    private LoginLogProvider loginLogProvider;
    
    private Filter filter;
    
    private String homePage;
    
    private String loginPage;
    
    private String logoutUrl;
    
    private String protectedUrl;
    
    private String systemUrl;
    
    private Filter[] filters = new Filter[0];
    
    public AuthenticateProvider getAuthenticateProvider() {
        return authenticateProvider;
    }
    
    public void setAuthenticateProvider(AuthenticateProvider authenticateProvider) {
        this.authenticateProvider = authenticateProvider;
    }
    
    public ClientVersionProvider getClientVersionProvider() {
		return clientVersionProvider;
	}

	public void setClientVersionProvider(ClientVersionProvider clientVersionProvider) {
		this.clientVersionProvider = clientVersionProvider;
	}

	public CaptchaValidator getCaptchaValidator() {
        return captchaValidator;
    }

    public void setCaptchaValidator(CaptchaValidator captchaValidator) {
        this.captchaValidator = captchaValidator;
    }

	public LoginLogProvider getLoginLogProvider() {
		return loginLogProvider;
	}

	public void setLoginLogProvider(LoginLogProvider loginLogProvider) {
		this.loginLogProvider = loginLogProvider;
	}

    public String getHomePage() {
        return homePage;
    }
    
    public void setHomePage(String homePage) {
        this.homePage = homePage;
    }
    
    public String getLoginPage() {
        return loginPage;
    }
    
    public void setLoginPage(String loginPage) {
        this.loginPage = loginPage;
    }
    
    public String getLogoutUrl() {
        return logoutUrl;
    }
    
    public void setLogoutUrl(String logoutUrl) {
        this.logoutUrl = logoutUrl;
    }
    
    public String getProtectedUrl() {
        return protectedUrl;
    }
    
    public void setProtectedUrl(String protectedUrl) {
        this.protectedUrl = protectedUrl;
    }
    
    public String getSystemUrl() {
        return systemUrl;
    }

    public void setSystemUrl(String systemUrl) {
        this.systemUrl = systemUrl;
    }

    public Filter[] getFilters() {
        return filters;
    }
    
    public void setFilters(Filter[] filters) {
        this.filters = filters;
    }
    
    public Filter getObject() throws Exception {
        return filter;
    }
    
    public Class<Filter> getObjectType() {
        return Filter.class;
    }
    
    public boolean isSingleton() {
        return true;
    }
    
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(authenticateProvider);
        
        LogoutProcessor logoutProcessor = new LogoutProcessor();
        if (!StringUtils.isEmpty(logoutUrl)) {
            logoutProcessor.setLogoutUrl(logoutUrl);
        }
        FilterNode filterNode = new FilterNode(logoutProcessor);
        
        OpenAuthPageAccessProcessor demoPageAccessProcessor = new OpenAuthPageAccessProcessor();
        if (!StringUtils.isEmpty(protectedUrl)) {
            demoPageAccessProcessor.setProtectedUrl(protectedUrl);
        }
        FilterNode demoPageAccessProcessorNode = new FilterNode(demoPageAccessProcessor);
        filterNode.setNext(demoPageAccessProcessorNode);
        
        AuthenticateProcessor authenticateProcessor = new AuthenticateProcessor();
        authenticateProcessor.setAuthenticateProvider(authenticateProvider);
        authenticateProcessor.setClientVersionProvider(clientVersionProvider);
        authenticateProcessor.setCaptchaValidator(captchaValidator);
        authenticateProcessor.setLoginLogProvider(loginLogProvider);
        FilterNode authenticateProcessorNode = new FilterNode(authenticateProcessor);
        demoPageAccessProcessorNode.setNext(authenticateProcessorNode);
                
        PrincipalResolver tokenResolver = new PrincipalResolver();
        if (!StringUtils.isEmpty(loginPage)) {
            tokenResolver.setLoginPage(loginPage);
        }
        FilterNode tokenResolverNode = new FilterNode(tokenResolver);
        authenticateProcessorNode.setNext(tokenResolverNode);
        
        AuthenticatedUrlProcessor authenticatedUrlProcessor = new AuthenticatedUrlProcessor();
        if (!StringUtils.isEmpty(homePage)) {
            tokenResolver.setHomePage(homePage);
        }
        FilterNode authenticatedUrlProcessorNode = new FilterNode(authenticatedUrlProcessor);
        tokenResolverNode.setNext(authenticatedUrlProcessorNode);
        
//        CheckAccessPermissionProcessor checkAccessPermissionProcessor = new CheckAccessPermissionProcessor();
//        if (!StringUtils.isEmpty(systemUrl)) {
//            checkAccessPermissionProcessor.setSystemUrl(systemUrl);
//        }
//        FilterNode checkAccessPermissionProcessorNode = new FilterNode(checkAccessPermissionProcessor);
//        authenticatedUrlProcessorNode.setNext(checkAccessPermissionProcessorNode);
        
        if (filters != null && filters.length > 0) {
            authenticatedUrlProcessorNode.setNext(to(filters));
//            checkAccessPermissionProcessorNode.setNext(to(filters));
        }
        
        filter = new SecurityFilterProxy(filterNode);
    }
    
    private FilterNode to(Filter[] filters) {
        FilterNode result = null;
        FilterNode current = null;
        for (Filter filter : filters) {
            if (result == null) {
                result = new FilterNode(filter);
                current = result;
                continue;
            }
            FilterNode next = new FilterNode(filter);
            current.setNext(next);
            current = next;
        }
        return result;
    }
    
}
